
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
#include <sys/epoll.h>
#include <netinet/tcp.h> // TCP_NODELAY需要包含这个头文件。
#include "InetAddress.h"
#include "Socket.h"
void setnonblocking(int fd)
{
    int event = fcntl(fd, F_GETFL);
    if (event == 0)
    {
        return;
    }
    fcntl(fd, F_SETFL, event | O_NONBLOCK);
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("usage: ./tcpepoll ip port\n");
        printf("example: ./tcpepoll 127.0.0.1 8080\n");
        return -1;
    }
    Socket servsock(createnonblocking());
    servsock.setkeepalive(true);
    servsock.setreuseaddr(true);
    servsock.setreuseport(true);
    servsock.settcpnodelay(true);
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);
    InetAddress serveraddr(ip, port);
    servsock.bind(serveraddr);
    servsock.listen();
    int epfd = epoll_create(1);

    struct epoll_event ev;
    ev.data.fd = servsock.fd();
    // 监听读事件
    ev.events = EPOLLIN;

    epoll_ctl(epfd, EPOLL_CTL_ADD, servsock.fd(), &ev);

    struct epoll_event evs[10]; // 这个数据是处理就绪的

    while (true) // 事件循环。
    {
        int infds = epoll_wait(epfd, evs, 10, -1); // 等待监视的fd有事件发生。

        // 返回失败。
        if (infds < 0)
        {
            perror("epoll_wait() failed");
            break;
        }

        // 超时。
        if (infds == 0)
        {
            printf("epoll_wait() timeout.\n");
            continue;
        }

        // 如果infds>0，表示有事件发生的fd的数量。
        for (int ii = 0; ii < infds; ii++) // 遍历epoll返回的数组evs。
        {
            if (evs[ii].events & EPOLLRDHUP) // 对方已关闭，有些系统检测不到，可以使用EPOLLIN，recv()返回0。
            {
                printf("client(eventfd=%d) disconnected.\n", evs[ii].data.fd);
                close(evs[ii].data.fd); // 关闭客户端的fd。
            } //  普通数据  带外数据
            else if (evs[ii].events & (EPOLLIN | EPOLLPRI)) // 接收缓冲区中有数据可以读。
            {
                if (evs[ii].data.fd == servsock.fd()) // 如果是listenFd有事件，表示有新的客户端连上来。
                {
                    InetAddress clientaddr;          
                    int clientfd = servsock.accept(clientaddr);
                    printf("accept client(fd=%d,ip=%s,port=%d) ok.\n", clientfd, clientaddr.ip(), clientaddr.port());

                    // 为新客户端连接准备读事件，并添加到epoll中。
                    ev.data.fd = clientfd;
                    ev.events = EPOLLIN | EPOLLET; // 边缘触发。
                    epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);
                    ////////////////////////////////////////////////////////////////////////
                }
                else // 如果是客户端连接的fd有事件。
                {
                    ////////////////////////////////////////////////////////////////////////
                    char buffer[1024];
                    while (true) // 由于使用非阻塞IO，一次读取buffer大小数据，直到全部的数据读取完毕。
                    {
                        bzero(&buffer, sizeof(buffer));
                        ssize_t nread = read(evs[ii].data.fd, buffer, sizeof(buffer));
                        if (nread > 0) // 成功的读取到了数据。
                        {
                            // 把接收到的报文内容原封不动的发回去。
                            printf("recv(eventfd=%d):%s\n", evs[ii].data.fd, buffer);
                            send(evs[ii].data.fd, buffer, strlen(buffer), 0);
                        }
                        else if (nread == -1 && errno == EINTR) // 读取数据的时候被信号中断，继续读取。
                        {
                            continue;
                        }
                        else if (nread == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) // 全部的数据已读取完毕。
                        {
                            break;
                        }
                        else if (nread == 0) // 客户端连接已断开。
                        {
                            printf("client(eventfd=%d) disconnected.\n", evs[ii].data.fd);
                            close(evs[ii].data.fd); // 关闭客户端的fd。
                            break;
                        }
                    }
                }
            }
            else if (evs[ii].events & EPOLLOUT) // 有数据需要写，暂时没有代码，以后再说。
            {
            }
            else // 其它事件，都视为错误。
            {
                printf("client(eventfd=%d) error.\n", evs[ii].data.fd);
                close(evs[ii].data.fd); // 关闭客户端的fd。
            }
        }
    }
    return 0;
}